package cn.xyfmzmy;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

/**
 * 自定义锁
 */
public class MyLock {


    // CAS原子变量，通过硬件级别的原子指令实现，保证线程安全，无锁化
    // 常用api: compareAndSet(预期值,新值),如果为预期值,就更新为新值
    AtomicBoolean flag = new AtomicBoolean(false);

    // 当前获取锁的线程
    Thread owner = null;

    // 用AtomicReference定义头节点(空节点)(可以让引用进行CAS操作，保证链表插入时的线程安全)
    AtomicReference<Node> head = new AtomicReference<>(new Node());

    // 尾结点
    AtomicReference<Node> tail = new AtomicReference<>(head.get());

    /**
     * 调用lock函数成功返回，就算拿到了锁；没有拿到锁，就在lock里面阻塞
     */
    void lock(){
         /*if(flag.compareAndSet(false,true)){
            owner = Thread.currentThread();
            System.out.println(Thread.currentThread().getName() + "直接获取锁");
            return;
        }*/

        // 如果开放上述代码可能会出现后来的线程直接获取锁，导致不公平锁
        // 对任何的当前线程，都存入链表的尾结点，可保证唤醒顺序，实现公平锁
        Node current = new Node();
        current.thread = Thread.currentThread();
        while (true){
            // 这里存在多线程竞争关系，currentTail可能不是最新的tail
            Node currentTail = tail.get();
            // 保证新的线程被安全的挂在尾结点上
            if(tail.compareAndSet(currentTail,current)){
                current.pre = currentTail;
                currentTail.next = current;
                System.out.println(Thread.currentThread().getName() + "获取锁失败,加入到了链表尾");
                break;
            }
        }
        while(true){
            // head -> A -> B -> C
            // 条件：当前节点的前驱节点为头结点 && 成功获取锁
            if(current.pre == head.get() && flag.compareAndSet(false,true)){
                // 更新当前持有锁的线程
                owner = Thread.currentThread();
                // 把当前节点变成head,断开相关链接
                head.set(current);
                current.pre.next = null;
                current.pre = null;
                System.out.println(Thread.currentThread().getName() + "被唤醒后，拿到锁");
                return;
            }
            // 阻塞等待,避免空转
            LockSupport.park();
        }

    }


    void unlock(){
        if(Thread.currentThread() != owner){
            throw new IllegalStateException("当前线程没有获取锁，不能释放");
        }
        // 释放锁，唤醒头结点的后继节点线程
        flag.set(false);
        Node headNode = head.get();
        Node next = headNode.next;
        if(next != null){
            System.out.println(Thread.currentThread().getName() + "释放锁后，唤醒了：" + next.thread.getName());
            LockSupport.unpark(next.thread);
        }
    }

    // 需要维护一个双向链表，记录没有获取锁的线程
    // 每次将没有获取锁的线程放在链表的尾部，释放锁时唤醒头部的线程
    class Node{
        Node pre;
        Node next;
        Thread thread;
    }

}
